package jobs

import (
	"dan/app/models"
	"dan/global"
	"dan/utils"
	"dan/utils/cronjob"
	"fmt"
	"github.com/robfig/cron/v3"
	"sync"
	"time"
)

var (
	timeFormat = "2021-05-20 13:14:21"
	retryCount = 3
	jobList map[string]JobExec
	lock sync.Mutex
)

type JobCore struct {
	InvokeTarget   string
	Name           string
	JobId          int
	EntryId        int
	CronExpression string
}

// 任务类型 http
type HttpJob struct {
	JobCore
}

type ExecJob struct {
	JobCore
}

func (e *ExecJob) Run() {
	startTime := time.Now()
	var obj = jobList[e.InvokeTarget]
	if obj == nil {
		global.JobLogger.Warning(" ExecJob Run job nil", e)
		return
	}
	CallExec(obj.(JobExec))
	endTime := time.Now()

	// 执行时间
	latencyTime := endTime.Sub(startTime)

	global.JobLogger.Info(time.Now().Format(timeFormat), " [INFO] JobCore ", e, "exec success , spend :", latencyTime)
}

func (h *HttpJob) Run() {
	startTime := time.Now()
	var count = 0
LOOP:
	if count < retryCount {
		str, err := utils.Get(h.InvokeTarget)
		if err != nil {
			// 如果失败暂停一段时间重试
			fmt.Println(time.Now().Format(timeFormat), " [ERROR] mission failed! ", err)
			fmt.Printf(time.Now().Format(timeFormat)+" [INFO] Retry after the task fails %d seconds! %s \n", time.Duration(count)*time.Second, str)
			time.Sleep(time.Duration(count) * time.Second)
			goto LOOP
		}
		count ++
	}
	// 结束时间
	endTime := time.Now()

	// 执行时间
	latencyTime := endTime.Sub(startTime)

	global.JobLogger.Info(time.Now().Format(timeFormat), " [INFO] JobCore ", h, "exec success , spend :", latencyTime)
}

func Setup()  {
	fmt.Println(time.Now().Format(timeFormat), " [INFO] JobCore Starting...")
	global.DanCron = cronjob.NewWithSeconds()
	sysJob := models.SysJob{}
	jobList, err := sysJob.GetList()
	if err != nil {
		fmt.Println(time.Now().Format(timeFormat), " [ERROR] JobCore init error", err)
	}
	if len(jobList) == 0 {
		fmt.Println(time.Now().Format(timeFormat), " [INFO] JobCore total:0")
	}
	_, err = sysJob.RemoveAllEntryID()
	if err != nil {
		fmt.Println(time.Now().Format(timeFormat), " [ERROR] JobCore remove entry_id error", err)
	}
	for i := 0; i < len(jobList); i++ {
		if jobList[i].JobType == 1 {
			j := &HttpJob{}
			j.InvokeTarget = jobList[i].InvokeTarget
			j.CronExpression = jobList[i].CronExpression
			j.JobId = jobList[i].JobId
			j.Name = jobList[i].JobName
			sysJob.EntryId, _ = AddJob(j)
		} else if jobList[i].JobType == 2 {
			j := &ExecJob{}
			j.InvokeTarget = jobList[i].InvokeTarget
			j.CronExpression = jobList[i].CronExpression
			j.JobId = jobList[i].JobId
			j.Name = jobList[i].JobName
			sysJob.EntryId, err = AddJob(j)
		}
		_, err = sysJob.Update(jobList[i].JobId)
	}

	global.DanCron.Start()
	fmt.Println(time.Now().Format(timeFormat), " [INFO] JobCore start success.")
	defer global.DanCron.Stop()
	select{}
}

func AddJob(job Job) (int, error) {
	if job == nil {
		fmt.Println("unknown")
		return 0, nil
	}

	return job.addJob()
}

func (h *HttpJob) addJob() (int, error) {
	id, err := global.DanCron.AddJob(h.CronExpression, h)
	if err != nil {
		fmt.Println(time.Now().Format(timeFormat), " [ERROR] JobCore AddJob error", err)
		return 0, err
	}
	EntryId := int(id)

	return EntryId, nil
}

func (h *ExecJob) addJob() (int, error) {
	id, err := global.DanCron.AddJob(h.CronExpression, h)
	if err != nil {
		fmt.Println(time.Now().Format(timeFormat), " [ERROR] JobCore AddJob error", err)
		return 0, err
	}
	EntryId := int(id)

	return EntryId, nil
}

func Remove(entryId int) chan bool {
	ch := make(chan bool)
	go func() {
		global.DanCron.Remove(cron.EntryID(entryId))
		fmt.Println(time.Now().Format(timeFormat), " [INFO] JobCore Remove success ,info entryID :", entryId)
		ch <- true
	}()

	return ch
}

func stop() chan bool {
	ch := make(chan bool)
	go func() {
		global.DanCron.Stop()
		ch <- true
	}()

	return ch
}